<?php
/**
 * @author spenceryg
 * @date 2023/3/18
 * @time 9:23 上午
 * @focus love & life
 */


namespace app\api1\controller;


use app\api\business\CommonBusiness;
use app\auto\model\Method;
use app\common\lib\ChatGpt;
use app\common\lib\verify\VerifyFactory;
use app\common\model\ArticleClass;
use app\common\model\GptStrategy;
use app\common\model\GptToken;
use app\common\model\ImgClass;
use app\common\model\Member;
use app\common\model\MemberInformation;
use app\common\model\MemberLoginLog;
use app\common\model\MemberPointsLog;
use app\common\model\PointProducts;
use app\wechat\lib\WechatQrcode;
use app\wechat\service\WechatLoginService;
use EasyWeChat\Factory;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use thans\jwt\facade\JWTAuth;
use think\facade\Db;
use think\response\Json;
use hg\apidoc\annotation as Apidoc;
use Yansongda\Pay\Pay;

error_reporting(E_ERROR | E_PARSE);

/**
 * Class Common
 * @package app\api\controller
 * @Apidoc\Title ("公共方法")
 */
class Common extends \app\BaseController
{

    public $verifyType = [
        0 => 'AliSmsVerify',
        1 => 'TencentSmsVerify',
        2 => 'EmailVerify'
    ];

    public $nickName = [
        ["name" => "James", "avatar" => "AI-dog.png"],
        ["name" => "Olivia", "avatar" => "Beagle-dog.png"],
        ["name" => "Emily", "avatar" => "Boxer-dog.png"],
        ["name" => "Ava", "avatar" => "Bulldog.png"],
        ["name" => "Mia", "avatar" => "Chinese-Pastoral-Dog.png"],
        ["name" => "Ethan", "avatar" => "German-Shepherd-dog.png"],
        ["name" => "Alexander", "avatar" => "Golden-Retriever-dog.png"],
        ["name" => "Sophia", "avatar" => "Labrador-Retriever-dog.png"],
        ["name" => "William", "avatar" => "Poodle-dog.png"],
        ["name" => "Charlotte", "avatar" => "Samoyed-dog.png"],
        ["name" => "Jacob", "avatar" => "Shiba-Inu-dog.png"],
        ["name" => "Harper", "avatar" => "Siberian-Husky-dog.png"],
        ["name" => "Michael", "avatar" => "Yorkshire-Terrier-dog.png"],
    ];


    /**
     * 获取随机头像和品种
     * getRandomNickName
     * @return array
     */
    private function getRandomNickName()
    {
        $length = sizeof($this->nickName);
        $random_index = rand(0, $length - 1);
        $name = $this->nickName[$random_index]['name'] . '-' . rand(1000, 9999);
        $avatar = "/dogs/" . $this->nickName[$random_index]['avatar'];
        return [$name, $avatar];
    }

    /**
     * 发送验证码
     * sendVerifyCode
     * NotHeaders
     * @Apidoc\Title("发送验证码")
     * @Apidoc\Query("to",default="18516335513", type="varchar", desc="手机号码|邮箱", require="1")
     * @Apidoc\Query("is_exits", type="int", default="0",  desc="0:不验证，1：必须存在，-1：必须不存在")
     * @Apidoc\Query("type", type="int",default="0",  require="", desc="0:阿里短信，1:腾讯短信， 2：邮件")
     */
    public function sendVerifyCode(): Json
    {
        $to = input('to', '', 'trim');
        $type = input('type', 0, 'intval');
        $is_exits = input('is_exits', 0, 'intval');
        $type = $this->verifyType[$type];
        $typeClass = '\app\common\lib\verify\\' . $type;
        $verifyClass = new $typeClass();
        try {
            $commonBusiness = new CommonBusiness();
            if ($is_exits == 1) {
                $commonBusiness->isMemberExist($to, $type);
            }
            if ($is_exits == -1) {
                $commonBusiness->isMemberNotExist($to, $type);
            }
            $commonBusiness->sendVerifyCode($to, $verifyClass);
            return success();
        } catch (\Exception $e) {
            return error($e->getMessage());
        }
    }


    private function decrypt($data, $key = 'mysecretkey1234567890')
    {
        list($encrypted_data, $iv) = explode('___', base64_decode($data), 2);
        return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
    }


    /**
     * login
     * @return Json|void
     *
     * NotHeaders
     * @Apidoc\Method ("post")
     * @Apidoc\Title ("登录")
     * @Apidoc\Param("to", require="1", type="varchar", desc="手机号")
     * @Apidoc\Param("verify", require="1", type="varchar", desc="验证码")
     * @Apidoc\Param("scene", type="varchar", desc="微信绑定场景")
     * @Apidoc\Returned("token", desc="登录token")
     */
    public function login()
    {
        try {
            $data = request()->only(['to', 'verify', 'official_openid']);
            validate(['to|手机号' => 'require', 'verify|验证码' => 'require'])->check($data);
//            VerifyFactory::verify($data['to'], $data['verify']);
            $member = Member::where('phone', $data['to'])->find();
            if (!$member) {
                $member = Member::create($data);
                $random_data = $this->getRandomNickName();
                Member::update([
                    'id' => $member->id,
                    'phone' => $data['to'],
                    'name' => $random_data[0],
                    'avatar' => $random_data[1],
                    'point_num' => (double)config('user.member_register_points'),
                    'number_of_free_chats' => config('chat.free_quantity'),
                    'ref' => substr(sha1($member->id), -8),
                ]);

                \app\common\model\MemberInformationRobot::create([
                    'member_id' => $member->id
                ]);

            }
            if ($data['official_openid']) {
                Member::update([
                    'id' => $member->id,
                    'union_id' => $data['official_openid'],
                ]);
            }
            //登录日志
            $ip = $this->request->ip();
            try {
                $address = (new Client())->get('https://api.topthink.com/ip/index?appCode=6204938a86d9806035aa4ed344d7f140&ip=' . $ip, ['http_errors' => false]);
                $address = $address->getBody()->getContents();
                $address = json_decode($address, true)['data'];
                $address = $address['Country'] . $address['Province'] . $address['City'] . ' ' . $address['Isp'];
            } catch (\Exception $e) {
                Log::error($e);
                $address = '未知';
            }
            MemberLoginLog::create([
                'member_id' => $member->id,
                'ip' => $ip,
                'address' => $address
            ]);
            // 生成token，参数为用户认证的信息，请自行添加
            $token = JWTAuth::builder(['uid' => $member->id]);
            return success('ok', [
                'token' => 'Bearer ' . $token
            ]);

        } catch (\Exception $e) {
            return error($e->getMessage());
        }

    }


    /**
     * vLogin
     * @Apidoc\Title ("模拟登录")
     * @return Json|void
     */
    public function vLogin()
    {
        $key = input('vkey');
        $member_id = cache($key);
        if ($member_id) {
            cache($key, null);
            // 生成token，参数为用户认证的信息，请自行添加
            $token = JWTAuth::builder(['uid' => $member_id]);
            return success('ok', [
                'token' => 'Bearer ' . $token,
                'id' => $member_id
            ]);
        }
        return error('无效的vkey');
    }


    /**
     * 获取二维码
     * getQrcode
     * NotHeaders
     * @Apidoc\Method ("get")
     * @Apidoc\Title ("获取微信登录二维码")
     * @Apidoc\Returned ("qrcode_url",desc="二维码网址")
     * @Apidoc\Returned ("qrcode_scene",desc="二维码场景")
     */
    public function getQrcode(): Json
    {
        $qrcode_data = WechatQrcode::getQrcode();
        return success('ok', $qrcode_data);
    }


    /**
     * isScanner
     * NotHeaders
     * @Apidoc\Method ("get")
     * @Apidoc\Title ("是否扫码")
     * @Apidoc\Returned ("status", desc="结果状态： fail=>未扫码， login=>登录成功 ， bind=>需要绑定手机")
     * @Apidoc\Returned ("token", desc="status==login, 登录成功的token")
     * @Apidoc\Returned ("union_id", desc="status==bind, 扫码成功的微信openid")
     */
    public function isScanner(): Json
    {
        $scene = input('scene', '', 'trim');
        if ($scene) {
            $res = WechatLoginService::getLastSceneData($scene);
            if (!$res) {
                return success('', [
                    'status' => 'fail'
                ]);
            }
            $member = Member::where('official_openid', $res['union_id'])->find();
            if ($member) {

                //登录日志
                $ip = $this->request->ip();
                try {
                    $address = (new Client())->get('https://api.topthink.com/ip/index?appCode=6204938a86d9806035aa4ed344d7f140&ip=' . $ip, ['http_errors' => false]);
                    $address = $address->getBody()->getContents();
                    $address = json_decode($address, true)['data'];
                    $address = $address['Country'] . $address['Province'] . $address['City'] . ' ' . $address['Isp'];
                } catch (\Exception $e) {
                    Log::error($e);
                    $address = '未知';
                }
                MemberLoginLog::create([
                    'member_id' => $member->id,
                    'ip' => $ip,
                    'address' => $address
                ]);

                $token = JWTAuth::builder(['uid' => $member->id]);
                return success('ok', [
                    'status' => 'login',
                    'token' => 'Bearer ' . $token
                ]);
            }
            // 适配 先小程序登录后 扫码登录清空
            $config = [
                'app_id' => config('wechat_official_account.app_id'),
                'secret' => config('wechat_official_account.secret'),
                'response_type' => 'array',
            ];
            $app = Factory::officialAccount($config);
            $user = $app->user->get($res['union_id']);
            if ($user['union_id']) {
                $member = Member::where('official_openid', $user['union_id'])->find();
                if ($member) {
                    // 绑定union_id
                    $member->official_openid = $res['union_id'];
                    $member->save();

                    //登录日志
                    $ip = $this->request->ip();
                    try {
                        $address = (new Client())->get('https://api.topthink.com/ip/index?appCode=6204938a86d9806035aa4ed344d7f140&ip=' . $ip, ['http_errors' => false]);
                        $address = $address->getBody()->getContents();
                        $address = json_decode($address, true)['data'];
                        $address = $address['Country'] . $address['Province'] . $address['City'] . ' ' . $address['Isp'];
                    } catch (\Exception $e) {
                        Log::error($e);
                        $address = '未知';
                    }
                    MemberLoginLog::create([
                        'member_id' => $member->id,
                        'ip' => $ip,
                        'address' => $address
                    ]);

                    $token = JWTAuth::builder(['uid' => $member->id]);
                    return success('ok', [
                        'status' => 'login',
                        'token' => 'Bearer ' . $token
                    ]);
                }
            }

            return success('', [
                'status' => 'bind',
                'official_openid' => $res['union_id']
            ]);
        } else {
            return success('', [
                'status' => 'fail'
            ]);
        }
    }


    /**
     * @Apidoc\Title("生成文案")
     * generate
     */
    public function generateCopywriting()
    {
        $event_name = input('event_name', 'chat', 'trim');
        if (!$event_name) {
            return error('事件名称能为空', []);
        }


        $key = input('key', '', 'trim');
        if (!$key || !cache($key)) {
            return error('key非法请求' . $key . '_' . cache($key), []);
        } else {
            $key_num = cache($key);
            $key_num--;
            cache($key, $key_num);
        }


        $data = cache($key . '_content');


        $copywriting_id = $data['copywriting_id'];


        $prompt = self::getPrompt($copywriting_id, $data);

        ChatGpt::chat($prompt, true, $event_name, false, false, true);
        return 1;
    }


    /**
     * @Apidoc\Title("生成文案socket版本")
     * generate
     */
    public static function generateCopywritingSocket($connection, $key): bool
    {
        if (!$key || !cache($key)) {
            $connection->send(json_encode([
                'type' => 'error',
                'msg' => 'key非法请求' . $key . '_' . cache($key)
            ]));
            return false;
        } else {
            cache($key, null);
        }

        $data = cache($key . '_content');
        cache($key . '_content', null);

        $copywriting_id = $data['copywriting_id'];

        $prompt = self::getPrompt($copywriting_id, $data);
        ChatGpt::chatSocket($prompt, $connection, false);
        return true;
    }


    /**
     * generateTitleSocket
     * @Apidoc\Title("生成标题socket版本")
     */
    public static function generateTitleSocket($connection, $key, $generated_quantity): bool
    {
        $key_word = '';
        if (!$key || !cache($key)) {
            $connection->send(json_encode([
                'type' => 'error',
                'msg' => 'key非法请求' . $key . '_' . cache($key)
            ]));
            return false;
        } else {
            $key_word = cache($key);
            cache($key, null);
        }

        $prompt = "给出{$generated_quantity}个关于'{$key_word}'的seo文章标题,结果列表以如下格式返回,每行一个。格式```1 文章标题```";
        ChatGpt::chatSocket($prompt, $connection, true);
        return true;
    }


    /**
     * generateChatMsg
     * @Apidoc\Title("生成聊天消息")
     * @return int|Json|void
     */
    public function generateChatMsg()
    {
        $event_name = input('event_name', 'chat', 'trim');
        if (!$event_name) {
            return error('事件名称能为空', []);
        }


        $key = input('key', '', 'trim');
        if (!$key || !cache($key)) {
            return error('key非法请求' . $key . '_' . cache($key), []);
        } else {
            $content = cache($key);
            cache($key, null);
        }

        $chat_id = input('chat_id');
        $chat = \app\common\model\Chat::find($chat_id);
        $last_msg = \app\common\model\ChatMessages::where('chat_id', $chat_id)
            ->order('id', 'desc')
            ->find();
        $prompt = $content;
        if (!$last_msg) {
            $system_prompt = $chat->initial_prompt_words;
            if ($system_prompt) {
                $prompt = [
                    ['role' => 'user', 'content' => str_replace('PROMPT', $content, $system_prompt)]
                ];
            }
        } else {
            if (mb_strlen($last_msg['content']) + mb_strlen($last_msg['response']) + mb_strlen($content) < 3800) {
                if($last_msg['response']){
                    $prompt = [
                        ['role' => 'user', 'content' => $last_msg['content']],
                        ['role' => 'assistant', 'content' => $last_msg['response']],
                        ['role' => 'user', 'content' => $content]
                    ];
                }else{
                    $prompt = [
                        ['role' => 'user', 'content' => $content]
                    ];
                }

            }
        }

        $this_msg = \app\common\model\ChatMessages::create([
            'chat_id' => $chat_id,
            'content' => $content,
            'member_id' => $chat['member_id'],
            'full_request' => json_encode($prompt)
        ]);


        $response = ChatGpt::chat($prompt, true, $event_name, false, false, true);
        $this_msg->response = $response;
        $this_msg->save();
        return 1;
    }


    /**
     * generateChatMsg
     * @Apidoc\Title("生成聊天消息")
     * @return int|Json|void
     */
    public static function generateChatMsgSocket($connection, $key, $chat_id)
    {

        if (!$key || !cache($key)) {
            $connection->send(json_encode([
                'type' => 'error',
                'msg' => 'key非法请求' . $key . '_' . cache($key)
            ]));
            return false;
        } else {
            $content = cache($key);
            cache($key, null);
        }

        $chat = \app\common\model\Chat::find($chat_id);
        $last_msg = \app\common\model\ChatMessages::where('chat_id', $chat_id)
            ->order('id', 'desc')
            ->find();
        $prompt = $content;
        if (!$last_msg) {
            $system_prompt = $chat->initial_prompt_words;
            if ($system_prompt) {
                $prompt = [
                    ['role' => 'user', 'content' => str_replace('PROMPT', $content, $system_prompt)]
                ];
            }
        } else {
            if (mb_strlen($last_msg['content']) + mb_strlen($last_msg['response']) + mb_strlen($content) < 3800) {
                if($last_msg['response']){
                    $prompt = [
                        ['role' => 'user', 'content' => $last_msg['content']],
                        ['role' => 'assistant', 'content' => $last_msg['response']],
                        ['role' => 'user', 'content' => $content]
                    ];
                }else{
                    $prompt = [
                        ['role' => 'user', 'content' => $content]
                    ];
                }
            }
        }

        $this_msg = \app\common\model\ChatMessages::create([
            'chat_id' => $chat_id,
            'content' => $content,
            'member_id' => $chat['member_id'],
            'full_request' => json_encode($prompt)
        ]);


        $response = ChatGpt::chatSocket($prompt, $connection, false);
        $this_msg->response = $response;
        $this_msg->save();
        return true;
    }


    /**
     * getPrompt
     * @param $copywriting_id
     * @return string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function getPrompt($copywriting_id, $data): string
    {
        $copywriting = \app\common\model\Copywriting::field(['custom_fields', 'name', 'picture', 'is_language', 'is_tone', 'cue_word'])->find($copywriting_id);
        $cue_word = $copywriting['cue_word'];
        $custom_fields = $copywriting['custom_fields'];
        $fields = explode("\r\n", $custom_fields);
        $user_fields = [];
        foreach ($fields as $field) {
            if ($field) {
                $user_fields[] = explode("__", $field);
            }
        }
        foreach ($user_fields as $field) {
            $cue_word = str_replace('{' . $field[0] . '}', $data[$field[0]], $cue_word);
        }
        $language = '';
        if ($copywriting['is_language']) {
            $language = " 请用{$data['language']}回答.";
        }
        $tone = '';
        if ($copywriting['is_tone']) {
            if ($data['tone']) {
                $tone = ' 请用' . $data['tone'] . '语气回答。';
            }
        }
        return $cue_word . $language . $tone;
    }


    /**
     * getOrderError
     * @Apidoc\Title("获取订单错误 为了调试")
     */
    public function getOrderError()
    {
        dump(cache('order'));
    }


    /**
     * miniPreLogin
     * @Apidoc\Title("小程序有注册预先登录")
     * @return Json|void
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function miniPreLogin()
    {
        $code = input('code');
        if (!$code) {
            return error('code不存在');
        }

        $config = [
            'app_id' => config('front_end.app_id'),
            'secret' => config('front_end.secret'),
            'response_type' => 'array',
        ];
        $app = Factory::miniProgram($config);
        $session = $app->auth->session($code);  // $code 是前端传递的登录凭证
        $openId = $session['openid'];
        $union_id = $session['unionid'];
        if ($openId) {
            // 直接有小程序openid登录
            $member = Member::where('mini_openid', $openId)->find();
            if ($member) {
                //登录日志
                $ip = $this->request->ip();
                try {
                    $address = (new Client())->get('https://api.topthink.com/ip/index?appCode=6204938a86d9806035aa4ed344d7f140&ip=' . $ip, ['http_errors' => false]);
                    $address = $address->getBody()->getContents();
                    $address = json_decode($address, true)['data'];
                    $address = $address['Country'] . $address['Province'] . $address['City'] . ' ' . $address['Isp'];
                } catch (\Exception $e) {
                    Log::error($e);
                    $address = '未知';
                }
//                MemberLoginLog::create([
//                    'member_id' => $member->id,
//                    'ip' => $ip,
//                    'address' => $address
//                ]);

                $token = JWTAuth::builder(['uid' => $member->id]);
                return success('ok', [
                    'status' => 'login',
                    'token' => 'Bearer ' . $token
                ]);
            }
            // 有union_id
            if ($union_id) {
                $member = Member::where('union_id', $union_id)->find();
                if ($member) {
                    // 绑定小程序openid
                    $member->mini_openid = $openId;
                    $member->save();

                    //登录日志
                    $ip = $this->request->ip();
                    try {
                        $address = (new Client())->get('https://api.topthink.com/ip/index?appCode=6204938a86d9806035aa4ed344d7f140&ip=' . $ip, ['http_errors' => false]);
                        $address = $address->getBody()->getContents();
                        $address = json_decode($address, true)['data'];
                        $address = $address['Country'] . $address['Province'] . $address['City'] . ' ' . $address['Isp'];
                    } catch (\Exception $e) {
                        Log::error($e);
                        $address = '未知';
                    }
//                    MemberLoginLog::create([
//                        'member_id' => $member->id,
//                        'ip' => $ip,
//                        'address' => $address
//                    ]);

                    $token = JWTAuth::builder(['uid' => $member->id]);
                    return success('ok', [
                        'status' => 'login',
                        'token' => 'Bearer ' . $token
                    ]);
                }
            }


            return success('ok', [
                'status' => 'info',
                'info' => $session
            ]);

        }

        return error('openid获取错误，程序异常请联系管理员');
    }


    /**
     * @Apidoc\Title("获取小程序跳转链接")
     * getUrlScheme
     * @return Json
     * @throws \EasyWeChat\Kernel\Exceptions\HttpException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getUrlScheme(): Json
    {
        $config = [
            'app_id' => config('front_end.app_id'),
            'secret' => config('front_end.secret'),
            'response_type' => 'array',
        ];
        $promotion_id = input('promotion_id');

        $app = Factory::miniProgram($config);
        $access_token = $app->access_token->getToken()['access_token'];
        $client = new Client();
        if($promotion_id){
            $res = $client->post("https://api.weixin.qq.com/wxa/generatescheme?access_token=".$access_token, [
                'json' => [
                    'jump_wxa' => [
                        'query' => 'ref=' . $promotion_id
                    ]
                ]
            ]);
        }else{
            $res = $client->post("https://api.weixin.qq.com/wxa/generatescheme?access_token=".$access_token);

        }
        $res = $res->getBody()->getContents();
        return success('', json_decode($res, true));

    }


    /**
     * @Apidoc\Title("微信小程序支付")
     * miniPay
     * @return Json|void
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function miniPay()
    {
        $code = input('code');
        $id = input('id');
        if(!$code || !$id){
            return error('参数错误！');
        }

        $config = [
            'app_id' => config('front_end.app_id'),
            'secret' => config('front_end.secret'),
            'response_type' => 'array',
        ];
        $app = Factory::miniProgram($config);
        $session = $app->auth->session($code);  // $code 是前端传递的登录凭证
        $openId = $session['openid'];


        $product = PointProducts::find($id);
        $subject = 'aidog充值购买' . ($product['integral'] + $product['reward_points']) .'积分';
        if($product['is_integral']){
            $subject =  'aidog充值购买' . $product['name'];
        }
        $result =  Pay::wechat(config('wechat_payment'))->miniapp([
            'out_trade_no' => input('uid'). '_' . $id .  '_' . time(),
            'total_fee' => $product['money'] * 100,
//            'total_fee' => 1,
            'body' => $subject,
            'openid' => $openId
        ]);
//        return success('',$session);
        return success('', $result);
    }


    /**
     * list
     * @Apidoc\Title("机器人产品列表")
     */
    public function list()
    {
        $robot_price = 0;
        $robot_id = input('robot_id', 0, 'intval');
        if($robot_id){
            $robot_price = \app\common\model\Robot::where('id', $robot_id)->value('daily_price');
        }
        $data = \app\common\model\RobotProducts::order('sort', 'asc')->where('is_open',1)->select();
        $data->each(function ($item) use ( $robot_price) {
            $daly_price = $item->price / $item->duration_days;
            $item->items = explode("\n", trim($item->functional_content));
        });
        return success('', $data);
    }


    /**
     * @Apidoc\Title("fix robotCode")
     */
    public function fixCode()
    {
        $robots = \app\common\model\Robot::select();
        foreach ($robots as $robot) {
            $robot->code = md5(time() . rand(1000, 9999) .  $robot->robot_products_id );
            $robot->save();
        }
        return success();
    }



}
